function WebGLCanvas(gl)
{
    var canvaswidth = undefined;
    var canvasheight = undefined;
    var board = new WebGLBoard(gl);
	var boardtokens = new WebGLBoardTokens(gl);
    var projection = new WebGLFloatArray(16);
    var view = new WebGLFloatArray(16);
    var boardwidth = undefined, boardheight = undefined;
    var mouseover = undefined;
	var bgcolor = undefined;
	
    this.update = function(player, game, fsm)
    {
        boardwidth = game.info.width;
        boardheight = game.info.height;
        board.update(player, game, fsm);
		boardtokens.update(player, game, fsm);
    	if (game.currentSide() == Side.RED) bgcolor = [0.5, 0.0, 0.0, 1.0];
		else bgcolor = [0.0, 0.0, 0.5, 1.0];
	}

    this.draw = function(width, height, lat, lon, dist)
    {
        canvaswidth = width;
        canvasheight = height;
        
        gl.viewport(0, 0, width, height);
        checkWebGLError(gl);
		gl.clearColor(bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3]);
        checkWebGLError(gl);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        checkWebGLError(gl);


        var aspect = (1.0*width) / height;
        projection.set(perspective(Math.PI / 4.0, aspect, 0.1, 100.0));
		view.set(orbit(lat, lon, dist));

        var above = view[9] > 0.0;
        
        gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE);
        
        if(above)
        {
            gl.enable(gl.BLEND);
            board.draw(projection, view, mouseover);
            gl.disable(gl.BLEND);
        }
		
		gl.enable(gl.DEPTH_TEST);
		boardtokens.draw(projection, view, mouseover);
		gl.disable(gl.DEPTH_TEST);
       
        if(!above)
        {
            gl.enable(gl.BLEND);
            board.draw(projection, view, mouseover);
            gl.disable(gl.BLEND);
		}
	}
    
    this.setMouseOver = function(mo)
    {
        mouseover = mo;
    }
    
    this.unproject = function(sx, sy)
    {
        var x = -1.0 + (2.0 * sx) / canvaswidth;
        var y = 1.0 - (2.0 * sy) / canvasheight;
        
        var mat = mmul(projection, view, 4);
        var inv = inverse(mat, 4);
        
        var org = mvmul(inv, [x, y, -1.0, 1.0], 4); // unproject     
        var tgt = mvmul(inv, [x, y, 1.0, 1.0], 4);
        tgt = svmul(1.0/tgt[3], tgt, 4); // homogenize
        org = svmul(1.0/org[3], org, 4);
        var dir = unit(vsub(tgt, org, 4), 4); // subtract
        
        // raycast
        if(Math.abs(dir[1]) < 1.0e-7) return undefined; // ray is coplanar to board
        var factor = -org[1] / dir[1]; // * dir[1] + org[1] == 0
        var hit = vadd(org, svmul(factor, dir, 4), 4);
        
        var boardx = Math.floor(hit[0] + boardwidth / 2.0);
        var boardy = Math.floor(hit[2] + boardheight / 2.0);
    
        return (
            (boardx >= 0 && boardx < boardwidth && boardy >= 0 && boardy < boardheight)
            ? [boardx, boardy]
            : undefined);
    }
}
